import math
import sys
import aiohttp
import asyncio
import datetime
from typing import Union, Annotated, List, Dict, Any, Tuple
import numpy as np

from log.logger import logger_error
from userException.paramException import ParamException
from userException.infoLackException import InfoLackException


class ShipInfo:
    def __init__(self, mmsi: int,
                 time_interval: Union[datetime.timedelta, None] = None,
                 start_time: Union[datetime.datetime, None] = None,
                 end_time: Union[datetime.datetime, None] = None):
        """

        :param mmsi : 船只编号
        :param time_interval : 往前的时间间隔,默认是90天
        :param start_time : 开始的时间
        :param end_time : 结束的时间,默认是当前时间
        3个变量不能同时指定
        """
        self.mmsi = mmsi
        if isinstance(time_interval, datetime.timedelta) and isinstance(end_time, datetime.datetime) and isinstance(start_time, datetime.datetime):
            logger_error.error("ShipInfo: 同时指定了time_interval, start_time, end_time")
            raise ParamException("time_interval, start_time, end_time不能同时被指定")
        if isinstance(time_interval, datetime.timedelta):
            self.time_interval = time_interval
        else:
            self.time_interval = datetime.timedelta(days=90)

        if isinstance(end_time, datetime.datetime) and isinstance(start_time, datetime.datetime):
            self.end_time = end_time
            self.start_time = start_time
        elif isinstance(start_time, datetime.datetime):
            self.start_time = start_time
            self.end_time = self.start_time + self.time_interval
        elif isinstance(end_time, datetime.datetime):
            self.end_time = self.end_time
            self.start_time = self.end_time - self.time_interval
        else:
            self.end_time = datetime.datetime.now()
            self.start_time = self.end_time - self.time_interval

        self.params1: Dict[str, Any] = {}
        self.params2: Dict[str, Any] = {}
        self.params1.update(mmsi=self.mmsi)
        self.params2.update(mmsi=self.mmsi)
        self.headers = {
            "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiTmFtZSI6ImppdWZhbmciLCJzdWIiOiJUb2tlbiIsImV4cCI6MjAzMzk3NDY1NywiaWF0IjoxNzE4NjE0NjU3fQ.3EfGvrYA4kElYWp5DQAXzEXmjtcpzsFZmxzZt8-8TFE"}
        # 4元组的列表,分别表示时间,经度,纬度,速度
        self.ship_info_list = []
        self.highest_level = None
        self.records = 0

    async def get_data(self, start_time: Union[datetime.datetime, None] = None, end_time: Union[datetime.datetime, None] = None, para: bool=False) -> List[Tuple]:
        """
        获取指定时间内的航行数据
        :param start_time: 起始时间
        :param end_time: 结束时间
        :param para: 是否是并发请求中的
        :return: 返回航行数据的列表,(时间, 纬度, 经度, 速度)
        """
        if not isinstance(start_time, datetime.datetime):
            start_time = self.start_time
        if not isinstance(end_time, datetime.datetime):
            end_time = self.end_time
        self.params1.update(startTime=start_time.strftime("%Y-%m-%d %H:%M:%S"), endTime=end_time.strftime("%Y-%m-%d %H:%M:%S"))

        # 获取指定船的航行数据
        async with aiohttp.ClientSession(headers=self.headers) as session:
            async with session.get('http://123.249.97.59:21673/api/position/history_ais', params=self.params1) as resp:
                # print(resp.status)
                # print(await resp.text())
                # print(await resp.json())
                result_dict = await resp.json()
                data = result_dict['data']
                if not data:
                    logger_error.error(f"ShipInfo: mmsi={self.mmsi}, no information for specific tanker")
                    raise InfoLackException("no information for specific tanker")
                info_list = data["shipPoints"]
                if not para:
                    if not info_list:
                        raise InfoLackException("no record for specific tanker")
                    elif len(info_list) == 1:
                        raise InfoLackException("only one record for specific tanker")
                    self.records = len(info_list)
                ship_info_list = []
                if info_list:
                    for info in info_list:
                        time = datetime.datetime.strptime(info['ti'], "%Y-%m-%d %H:%M:%S")
                        latitude = float(info['la'])
                        longitude = float(info['lo'])
                        speed = float(info['sp'])
                        ship_info_list.append((time, latitude, longitude, speed))
        return ship_info_list

    async def get_data_parallel(self, num: int = 3) -> List[Tuple]:
        """
        并发的从上游请求数据
        :param num: 并发数
        :return: 返回航行数据的列表,(时间, 纬度, 经度, 速度)
        """
        start_ts = self.start_time.timestamp()
        end_ts = self.end_time.timestamp()

        ts_list = list(np.linspace(start=start_ts, stop=end_ts, num=num + 1))
        start_list = [datetime.datetime.fromtimestamp(x) for x in ts_list[:-1]]
        end_list = [datetime.datetime.fromtimestamp(x) for x in ts_list[1:]]

        coroutines = []
        for i in range(num):
            coroutines.append(asyncio.create_task(self.get_data(start_list[i], end_list[i], para=True)))
        # for i in range(num):
        #     self.ship_info_list += await coroutines[i]
        results = await asyncio.gather(*coroutines)
        for r in results:
            self.ship_info_list += r
        if not self.ship_info_list:
            raise InfoLackException("no record for specific tanker")
        elif len(self.ship_info_list) == 1:
            raise InfoLackException("only one record for specific tanker")
        self.records = len(self.ship_info_list)
        return self.ship_info_list

    async def get_highest_level(self):
        """
        从上游请求指定船的最高航速
        :return:
        """
        async with aiohttp.ClientSession(headers=self.headers) as session:
            async with session.get('http://123.249.97.59:21673/api/raw/vessels_record', params=self.params2) as resp:
                result_dict = await resp.json()
                self.highest_level = math.floor(result_dict['data']['speed'])